package com.smart.tool.extension.processor;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

import com.smart.tool.extension.ExtensionAnnotation;
import com.smart.tool.extension.model.ExtensionSence;
import com.smart.tool.extension.repository.IExtensionRepository;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * spring 项目启动过程中加载扩展点实现类
 */
public class ExtensionProcessor implements BeanPostProcessor {


    @Autowired
    private IExtensionRepository iExtensionRepository;


    /**
     * bean对象初始化之前进行加载
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        doRegister(bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    private void doRegister(Object bean) {

        ExtensionAnnotation extensionAnnotation = bean.getClass().getAnnotation(ExtensionAnnotation.class);

        if (extensionAnnotation == null) {
            return;
        }
        Class<?> clazz = bean.getClass();
        ExtensionSence extensionSence = ExtensionSence.of(extensionAnnotation.scene(), extensionAnnotation.useCase());
        extensionSence.setClazz(clazz);
        extensionSence.setInterfaceClasses(getAllInterface(clazz));
        extensionSence.setSuperClasses(getAllSuperClass(clazz));
        extensionSence.setType(extensionAnnotation.type());
        iExtensionRepository.loadExtension(bean, extensionSence);

    }

    /**
     * 获取所有父类，实现的接口，继承的父类
     * @param clazz
     * @return
     */
    private Set<Class<?>> getAllSuperClass(Class<?> clazz) {

        Set<Class<?>> classSet = new HashSet<>();

        Class<?> copyClazz = clazz;
        while (copyClazz != null) {
            classSet.add(copyClazz);
            copyClazz = copyClazz.getSuperclass();
        }


        return classSet;
    }

    private Set<Class<?>> getAllInterface(Class<?> clazz) {
        Set<Class<?>> classSet = new HashSet<>();
        Class<?>[] interfaces = clazz.getInterfaces();

        Stack<Class<?>> stack = new Stack<>();
        stack.addAll(Arrays.stream(interfaces).collect(Collectors.toList()));
        while(!stack.isEmpty()) {
            Class<?> interfaceClass = stack.pop();
            classSet.add(interfaceClass);
            Class<?>[] tmpInterfaces = interfaceClass.getInterfaces();
            if(tmpInterfaces!=null&&tmpInterfaces.length>0) {
                stack.addAll(Arrays.stream(tmpInterfaces).collect(Collectors.toList()));
            }
        }
        return classSet;
    }
}
